-
Notifications
You must be signed in to change notification settings - Fork 30
Sentinel #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Sentinel #70
Conversation
|
I forgot to add. Here is how you'd use the API: // direct connection, paired, reconnecting
use redis_async::client::{RedisConnectionBuilder, paired::PairedConnectionBuilder};
let client = RedisConnectionBuilder::new("url")
.username("hello")
.password("world")
.paired_reconnecting()
.await
.unwrap();
let response = client.send(data).await.unwrap();
// connecting through sentinel, pubsub, not reconnecting
use redis_async::client::{SentinelConnectionBuilder, pubsub::PubsubConnectionBuilder};
let client = SentinelConnectionBuilder::new(vec!["host 1", "host 2"], "master_name")
.redis_username("foo")
.redis_password("bar")
.pubsub_connect()
.await
.unwrap();
let stream = client.subscribe("message").await.unwrap(); |
|
Hello, Thanks for this 👍 This is quite a big PR so I'll some more comments later once I have the chance to go in to some of the details in a bit more detail. But some high-level comments: Regarding reconnection - I think I would prefer if the default path maintained backward compatibility, just to reduce the risk of users not noticing the modified behaviour. But I could be persuaded otherwise if non-reconnecting clients was the idiom, but I think most Redis clients I've used for other programming languages have automatically handled reconnection. So on balance, I think that should probably be the default and non-reconnecting made the option. I do like the new reconnection logic. It's much cleaner than what was there before, which grew organically for several versions and is currently quite gnarly. One thing I'm not sure about how the library should behave in future is the let set_future = con.send(resp_array!["SET", "x", "12"]);
let get_result = con.send(resp_array!["GET", "x"]).await;Secondly, it enabled the implicit pipelining to occur (as described in the project's README). So I'm not sure where we should go with this. On the one hand, by making
My first thought for this one is it should be fail-safe in the sense that we don't want it to be possible for the state the be I'll check out the PR locally in more detail over the next few days as time allows. Thanks again 👍 |
Agree. Let's not confuse people.
Do you mean sending multiple messages without waiting for responses? Like this? I believe this still works, but you have to fire multiple futures at once. It might be even more intuitive, if you compare it to sleep for example:
My thinking here was that we'll probably want to detect disconnects in paired connect anyway to cancel/fail request futures. And if we're doing that, it would be really easy to notify reconnect. In addition, complex connections could trigger reconnect if they find themselves in some unrecoverable state. |
This PR is far from being ready for merge. Its purpose it to show how we might make the library more flexible and add support for Sentinel. Check out issue 67 for the background.
This is a pretty big change to the internals of the library. Public API has also changed, although mostly in terms of types - I wanted to keep the feel of the original.
So, what has changed?
Builders
Previously, there was only one builder:
ConnectionBuilder. It did not hold data necessary for connecting to Redis through sentinel (redis master name, sentinels address list, credentials). Now there are two builders:RedisConnectionBuilderandSentinelConnectionBuilder. They have different fields and methods, but implement the same trait:ConnectionBuilder.ConnectionBuildertrait requires one method,connect, which establishes a primitive (RespConnection) connection to Redis. WhileRedisConnectionBuilderdoes nothing fancy (simply calls the oldconnect_with_authfunction),SentinelConnectionBuilderasks all sentinels for Redis master address and connects to it after double checking Redis role.Similarly to how it worked before, any type implementing
ConnectionBuildercan start paired or pubsub connection by callingpaired_connectorpubsub_connecton the builder. The only difference is that now users will have to add an appropriate trait to the scope, that is eitherclient::paired::PairedConnectionBuilderorclient::pubsub::PubsubConnectionBuilder. Internally, these methods create primitive connection and upgrade it to a complex connection (Paired/Pubsub).Reconnecting
Until now, one couldn't opt out of reconnecting in
PairedConnectionandPubsubConnection. That is not the case anymore. Both connection types can be created fromRespConnectionand error channel. More on that in a second. I've addedReconnectstruct holding builder and connection state.Reconnectis generic overConnectionBuilderandComplexConnection.ComplexConnectionis a trait with pretty loose requirements: implemeneters must be able to construct themselves fromRespConnectionand error channel. Error channel is a oneshot sender that informsReconnectstruct that the connection should be recreated (using the builder). Users can access the currently active complex connection by calling.currentonReconnecting. Because we make few assumptions about the complex connection, it's easy to useReconnectwith your own code. For convenience,PairedConnectionaddssendandsend_and_forgettoReconnectholdingPairedConnection.PubsubConnectioncould do the same.Side effects
I made functions/methods like
paired_connectreturn non-reconnecting connections and addedpaired_reconnecting. People migrating to a new version might not notice that. Maybe we should keep old functions as they were and addpaired_non_reconnetinginstead?Considerations
Is error channel a good way of informing
Reconnectabout errors? Maybe we should wrapRespConnectionwith some struct that spies on the tcp channel and and pass that to complex connection? There might be a use case for having that level control in complex connection.Maybe
Reconnectshould not be generic overConnectionBuilderbut instead storeBox<dyn ConnectionBuilder>? This way we wouldn't have to mention the builder type when describing the type of a Connection, e.g.vs
I think we can afford that. We'd possibly have to relax trait bounds.
What do you think of this design?